﻿using System;
using System.Collections.Generic;
using System.Linq;
using Eg.Core;
using NHibernate;

namespace MultiCriteria {

  public class Queries {

    private readonly ISession _session;

    public Queries(ISession session) {
      if (session == null)
        throw new ArgumentNullException("session");
      _session = session;
    }

    public struct PageOf<T> {
      public int PageCount;
      public int PageNumber;
      public IEnumerable<T> PageOfResults;
    }

    public PageOf<Product> GetPageOfProducts(int pageNumber, int pageSize) {
      var skip = (pageNumber - 1) * pageSize;

      var countQuery = GetCountQuery();
      var resultQuery = GetPageQuery(skip, pageSize);

      // combine queries
      var multiCrit = _session.CreateMultiCriteria()
        .Add<int>("count", countQuery)
        .Add<Product>("page", resultQuery);
      // queries were not yet executed

      var productCount = ((IList<int>)multiCrit.GetResult("count")).Single();    // <--- executes both queries
      var products = (IList<Product>)multiCrit.GetResult("page");                // <--- just take the result from memory

      var pageCount = (int)Math.Ceiling(productCount / (double)pageSize);

      return new PageOf<Product>() {
        PageCount = pageCount,
        PageOfResults = products,
        PageNumber = pageNumber
      };
    }

    // query to count all the products in the database 
    private ICriteria GetCountQuery() {
      return _session.QueryOver<Product>()
        .SelectList(list => list.SelectCount(m => m.Id))
        .UnderlyingCriteria;
    }

    // returns a "page", sorted by unit price 
    private ICriteria GetPageQuery(int skip, int take) {
      return _session.QueryOver<Product>()
        .OrderBy(m => m.UnitPrice).Asc
        .Skip(skip) // <--- NH Skip and Take
        .Take(take)
        .UnderlyingCriteria;
    }

  }

}
